import { Controller, Get, Req, Res, Query, Logger, Headers } from "@nestjs/common";
import { Request, Response } from "express";
import {
    clientOauth2AcessToken,
    clientOauth2IdToken,
    clientOauth2RefreshToken,
    OAuthIsSkipLabel,
    refreshTokenMaxAge,
} from "../const";
import { ConfigService } from "../config/config.service";
import { getIsMobile, getServiceNameFromApi, getErrorCodeFromService } from "./utils";
@Controller()
export class SsoController {
    constructor(private readonly configService: ConfigService, private readonly logger: Logger) {}

    @Get("/")
    async SingleSignOn(
        @Query() query,
        @Req() req: Request,
        @Res() res: Response,
        @Headers("user-agent") userAgent: string
    ): Promise<void> {
        const isMobile = getIsMobile(userAgent);
        try {
            const redirtctURL = isMobile ? "/anyshare/m/home" : "/anyshare/dir";
            const isSkip = req.cookies?.[OAuthIsSkipLabel] === "true" ? true : false;
            if (Object.keys(query).length) {
                let lastTimestamp = Date.now();
                this.logger.log(`{/eacp/v1/auth1/getconfig POST} start`);
                const {
                    data: { thirdauth },
                } = await this.configService.eacpPublicApi.post("/eacp/v1/auth1/getconfig");
                this.logger.log(`{/eacp/v1/auth1/getconfig POST} success  +${Date.now() - lastTimestamp}ms`);
                lastTimestamp = Date.now();
                this.logger.log(`{/authentication/v1/sso POST} start`);
                const {
                    data: { access_token, expirses_in, refresh_token, id_token },
                } = await this.configService.authenticationPublicApi.post(
                    "/authentication/v1/sso",
                    {
                        client_id: this.configService.hydraInfo.clientId,
                        redirect_uri: this.configService.hydraInfo.redirectUri,
                        response_type: "token id_token",
                        scope: "offline openid all",
                        credential: {
                            id: thirdauth && thirdauth.id,
                            params: query,
                        },
                    },
                    {
                        headers: {
                            "X-Forwarded-For": req.headers["x-forwarded-for"] || "",
                        },
                    }
                );
                this.logger.log(`{/authentication/v1/sso POST} success  +${Date.now() - lastTimestamp}ms`);

                const isMaxAge = Number(expirses_in) || Number(expirses_in) === 0 || Number(expirses_in) === -0;
                const isSetSessionExpires = isMaxAge && isSkip;
                const maxAge = isSetSessionExpires ? { maxAge: expirses_in * 1000 } : {};
                const refreshMaxAge = isSetSessionExpires ? { maxAge: refreshTokenMaxAge } : {};

                res.cookie(clientOauth2AcessToken, access_token, {
                    httpOnly: false,
                    secure: this.configService.publicInfo.https,
                    ...maxAge,
                });
                res.cookie(clientOauth2IdToken, id_token, {
                    httpOnly: false,
                    secure: this.configService.publicInfo.https,
                    ...maxAge,
                });
                res.cookie(clientOauth2RefreshToken, refresh_token, {
                    httpOnly: false,
                    secure: this.configService.publicInfo.https,
                    ...refreshMaxAge,
                });

                return res.redirect(redirtctURL);
            } else {
                return res.redirect(`/anyshare/oauth2/login?redirect=${encodeURIComponent(redirtctURL)}&sso=true`);
            }
        } catch (e) {
            const path = e && e.request && (e.request.path || (e.request._options && e.request._options.path));
            this.logger.error(`{${path} POST} error `, JSON.stringify(e && e.response && e.response.data));
            if (e && e.response && e.response.status !== 503) {
                const redirectURL = isMobile ? "/anyshare/m/" : "/anyshare/";
                const { status, data } = e.response;
                res.statusCode = status;
                if (data.code === 401001004 || data.code === 403001030) {
                    return res.redirect(`${redirectURL}?errorcode=${data.code}`);
                } else {
                    res.render("login-err", {
                        url: `/oauth2/error?${
                            (
                                data &&
                                Object.keys(data).map(
                                    (key) => `${encodeURIComponent(key)}=${encodeURIComponent(data[key])}`
                                )
                            ).join("&") || ""
                        }`,
                    });
                }
            } else {
                const service = getServiceNameFromApi(path);
                this.logger.error(`内部错误，连接${service}服务失败`);
                res.statusCode = 500;
                res.render("login-err", {
                    url: `/oauth2/error?code=${getErrorCodeFromService(service)}&cause=${encodeURIComponent(
                        "内部错误"
                    )}&message=${encodeURIComponent(`连接${service}服务失败`)}`,
                });
            }
        }
    }
}
